001 /* 002 * Copyright 2003-2005 The Apache Software Foundation 003 * Copyright 2005 Stephen McConnell 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package net.dpml.cli.option; 018 019 import java.util.Iterator; 020 import java.util.ListIterator; 021 import java.util.Set; 022 023 import net.dpml.cli.DisplaySetting; 024 import net.dpml.cli.Option; 025 import net.dpml.cli.WriteableCommandLine; 026 import net.dpml.cli.resource.ResourceConstants; 027 import net.dpml.cli.resource.ResourceHelper; 028 029 /** 030 * A base implementation of Option providing limited ground work for further 031 * Option implementations. 032 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 033 * @version 1.0.0 034 */ 035 public abstract class OptionImpl implements Option 036 { 037 private final int m_id; 038 private final boolean m_required; 039 040 /** 041 * Creates an OptionImpl with the specified id 042 * @param id the unique id of this Option 043 * @param required true iff this Option must be present 044 */ 045 public OptionImpl( final int id, final boolean required ) 046 { 047 m_id = id; 048 m_required = required; 049 } 050 051 /** 052 * Indicates whether this Option will be able to process the particular 053 * argument. The ListIterator must be restored to the initial state before 054 * returning the boolean. 055 * 056 * @see #canProcess(WriteableCommandLine,String) 057 * @param commandLine the CommandLine object to store defaults in 058 * @param arguments the ListIterator over String arguments 059 * @return true if the argument can be processed by this Option 060 */ 061 public boolean canProcess( 062 final WriteableCommandLine commandLine, final ListIterator arguments ) 063 { 064 if( arguments.hasNext() ) 065 { 066 final String argument = (String) arguments.next(); 067 arguments.previous(); 068 return canProcess( commandLine, argument ); 069 } 070 else 071 { 072 return false; 073 } 074 } 075 076 /** 077 * Returns a string representation of the option. 078 * @return the string value 079 */ 080 public String toString() 081 { 082 final StringBuffer buffer = new StringBuffer(); 083 appendUsage( buffer, DisplaySetting.ALL, null ); 084 return buffer.toString(); 085 } 086 087 /** 088 * Returns the id of the option. This can be used in a loop and switch 089 * construct: 090 * 091 * <code> 092 * for(Option o : cmd.getOptions()){ 093 * switch(o.getId()){ 094 * case POTENTIAL_OPTION: 095 * ... 096 * } 097 * } 098 * </code> 099 * 100 * The returned value is not guarenteed to be unique. 101 * 102 * @return the id of the option. 103 */ 104 public int getId() 105 { 106 return m_id; 107 } 108 109 /** 110 * Evaluate this instance against the supplied instance for equality. 111 * @param thatObj the other object 112 * @return true if the supplied instance is equal to this instance 113 */ 114 public boolean equals( final Object thatObj ) 115 { 116 if( thatObj instanceof OptionImpl ) 117 { 118 final OptionImpl that = (OptionImpl) thatObj; 119 return ( getId() == that.getId() ) 120 && equals( getPreferredName(), that.getPreferredName() ) 121 && equals( getDescription(), that.getDescription() ) 122 && equals( getPrefixes(), that.getPrefixes() ) 123 && equals( getTriggers(), that.getTriggers() ); 124 } 125 else 126 { 127 return false; 128 } 129 } 130 131 private boolean equals( Object left, Object right ) 132 { 133 if( ( left == null ) && ( right == null ) ) 134 { 135 return true; 136 } 137 else if( ( left == null ) || ( right == null ) ) 138 { 139 return false; 140 } 141 else 142 { 143 return left.equals( right ); 144 } 145 } 146 147 /** 148 * Return the hashcode value for this instance. 149 * @return the hash value 150 */ 151 public int hashCode() 152 { 153 int hashCode = getId(); 154 hashCode = ( hashCode * 37 ) + getPreferredName().hashCode(); 155 if( getDescription() != null ) 156 { 157 hashCode = ( hashCode * 37 ) + getDescription().hashCode(); 158 } 159 hashCode = ( hashCode * 37 ) + getPrefixes().hashCode(); 160 hashCode = ( hashCode * 37 ) + getTriggers().hashCode(); 161 return hashCode; 162 } 163 164 /** 165 * Recursively searches for an option with the supplied trigger. 166 * 167 * @param trigger the trigger to search for. 168 * @return the matching option or null. 169 */ 170 public Option findOption( String trigger ) 171 { 172 if( getTriggers().contains( trigger ) ) 173 { 174 return this; 175 } 176 else 177 { 178 return null; 179 } 180 } 181 182 /** 183 * Indicates whether this option is required to be present. 184 * @return true if the CommandLine will be invalid without this Option 185 */ 186 public boolean isRequired() 187 { 188 return m_required; 189 } 190 191 /** 192 * Adds defaults to a CommandLine. 193 * 194 * Any defaults for this option are applied as well as the defaults for 195 * any contained options 196 * 197 * @param commandLine the CommandLine object to store defaults in 198 */ 199 public void defaults( final WriteableCommandLine commandLine ) 200 { 201 // nothing to do normally 202 } 203 204 /** 205 * Check prefixes. 206 * @param prefixes the prefixes set 207 */ 208 protected void checkPrefixes( final Set prefixes ) 209 { 210 // nothing to do if empty prefix list 211 if( prefixes.isEmpty() ) 212 { 213 return; 214 } 215 216 // check preferred name 217 checkPrefix( prefixes, getPreferredName() ); 218 219 // check triggers 220 getTriggers(); 221 222 for( final Iterator i = getTriggers().iterator(); i.hasNext();) 223 { 224 checkPrefix( prefixes, (String) i.next() ); 225 } 226 } 227 228 /** 229 * Check prefixes. 230 * @param prefixes the prefixes set 231 * @param trigger the trigger 232 */ 233 private void checkPrefix( final Set prefixes, final String trigger ) 234 { 235 for( final Iterator i = prefixes.iterator(); i.hasNext();) 236 { 237 String prefix = (String) i.next(); 238 if( trigger.startsWith( prefix ) ) 239 { 240 return; 241 } 242 } 243 244 final ResourceHelper helper = ResourceHelper.getResourceHelper(); 245 final String message = 246 helper.getMessage( 247 ResourceConstants.OPTION_TRIGGER_NEEDS_PREFIX, 248 trigger, 249 prefixes.toString() ); 250 throw new IllegalArgumentException( message ); 251 } 252 }